package operate

import (
	"fmt"
	"go.manyji.com/lib/app"
	"go.manyji.com/lib/config"
	"go.manyji.com/lib/encoding/json"
	"go.manyji.com/lib/formatStruct"
	"go.manyji.com/lib/log"
	"go.manyji.com/lib/rand"
	"go.manyji.com/lib/util/convert"
	uTime "go.manyji.com/lib/util/time"
	"path/filepath"
	"sieve_proc/constant"
	"sieve_proc/data"
	"sieve_proc/filter"
	"sieve_proc/util"
	"strings"
	"time"
)

var (
	orderCnt   = config.Int("server.orderCnt", 10, "")
	sieveDebug = config.Bool("sieve.debug", false, "sieve debug")
)

type Operator interface {
	CreateTaskData(string, []string) (string, error)
	UpdateExtra(string) Operator
	batchInsertPhoneData() error
	GetTCnt() int
	LoadOrderData(id int) (*Op_Order, error)
	GetTag() string
	updateTag(string)
}

func NewOperator(op string) Operator {
	var ret Operator = newFilter(op)
	if ret != nil {
		ret.updateTag(op)
	}
	return ret
}

type Op_Base struct {
	Tag       string
	TableName string
	Extra     string
	PhoneList []string
	OrderInfo *Op_Order
}

type Op_Order struct {
	Id          int
	CountryCode string
	TCnt        int
	SCnt        int
	FCnt        int
	Status      int
	CreateTime  int64
	TaskId      string
	OTag        string `field:"-"`
	Extra       string
	Ret         string
}

func (this *Op_Base) CreateTaskData(countryCode string, phoneList []string) (string, error) {
	cnt := len(phoneList)
	if cnt == 0 {
		return "", fmt.Errorf("phone was empty")
	}
	this.PhoneList = phoneList
	nowTime := time.Now().Unix()
	row, err := data.DB().Exec(fmt.Sprintf("INSERT INTO %s(CountryCode,TCnt,SCnt,Extra,Status,CreateTime,UpdateTime,FCnt)VALUES(?,?,?,?,?,?,?,?)", this.TableName), countryCode, cnt, 0, this.Extra, 0, nowTime, 0, 0)
	if err != nil {
		return "", err
	}
	id, err := row.LastInsertId()
	if err != nil {
		return "", err
	}
	this.OrderInfo = &Op_Order{
		Id:          int(id),
		CountryCode: countryCode,
		TCnt:        cnt,
		Status:      0,
		SCnt:        0,
		CreateTime:  nowTime,
	}
	err = this.batchInsertPhoneData()
	if err != nil {
		return "", err
	}
	data.DB().Exec(fmt.Sprintf("UPDATE %s set Status=1 where Id=?", this.TableName), this.OrderInfo.Id)
	return this.Tag + "_" + convert.ToString(id), nil
}

func (this *Op_Base) UpdateExtra(e string) Operator {
	this.Extra = e
	return this
}
func (this *Op_Base) GetTCnt() int {
	return this.OrderInfo.TCnt
}
func (this *Op_Base) GetTag() string {
	return this.Tag
}
func (this *Op_Base) updateTag(tag string) {
	this.Tag = tag
}
func (this *Op_Base) batchInsertPhoneData() error {
	table := this.OrderInfo.GetFilterTableName(this.TableName)
	beginIndex := 0
	for {
		endIndex := beginIndex + 2000
		breakFlag := false
		if endIndex > len(this.PhoneList) {
			endIndex = len(this.PhoneList)
			breakFlag = true
		}
		nList := this.PhoneList[beginIndex:endIndex]
		if len(nList) == 0 {
			break
		}
		fieldValue := []interface{}{}
		sql := fmt.Sprintf("INSERT INTO  `%s`(`OrderId`,`PhoneNum`)VALUES", table)
		for _i, phone := range nList {
			if _i > 0 {
				sql += ","
			}
			sql += "(?,?)"
			fieldValue = append(fieldValue, this.OrderInfo.Id, phone)
		}
		_, err := data.DB().Exec(sql, fieldValue...)
		if err != nil {
			return err
		}
		if breakFlag {
			break
		}
		beginIndex = endIndex
	}
	return nil
}

func (this *Op_Base) LoadOrderData(id int) (*Op_Order, error) {
	if id == 0 {
		return nil, fmt.Errorf("订单数据有误")
	}
	fp := formatStruct.ParseStructFields(this.OrderInfo)
	err := data.DB().QueryRow(fmt.Sprintf("SELECT %s from %s where id=?", fp.ColsToString(), this.TableName), id).Scan(fp.GetPtrs()...)
	if err != nil {
		return nil, err
	}
	this.OrderInfo.updateTag(this.GetTag())
	return this.OrderInfo, nil
}

func (this *Op_Order) GetFilterTableName(tName string) string {
	tableBaseName := tName + "_phone"
	tableName := tableBaseName + uTime.FmtDate(time.Unix(this.CreateTime, 0), "Ymd")
	_, err := data.DB().Exec(fmt.Sprintf("CREATE TABLE IF NOT EXISTS `%s` like %s", tableName, tableBaseName))
	if err != nil {
		log.Err(fmt.Errorf("op:%s,tableName:%s,orderId:%d,err:%s", "GetFilterTableName", tName, this.Id, err))
	}
	return tableName
}
func (this *Op_Order) GetTagId() string {
	return fmt.Sprintf("%s_%d", this.OTag, this.Id)
}
func (this *Op_Order) updateTag(tag string) {
	this.OTag = tag
}
func (this *Op_Order) GetId() int {
	return this.GetId()
}

func (this *Op_Order) GetPhoneList() []string {
	list := []string{}
	rows, err := data.DB().Query(fmt.Sprintf("SELECT PhoneNum from %s where OrderId=?", this.GetFilterTableName(getTableName(this.OTag))), this.Id)
	if err != nil {
		log.Err(this, err)
		return list
	}
	defer rows.Close()
	for rows.Next() {
		var phone string
		err = rows.Scan(&phone)
		if err != nil {
			continue
		}
		list = append(list, phone)
	}
	return list
}

func (this *Op_Order) doTaskData() error {
	if *sieveDebug {
		return nil
	}
	if this.Status == 2 {
		return nil
	}
	this.Status = 2
	var (
		taskId string
		err    error
	)
	switch this.OTag {
	case constant.WsfTag:
		taskId, err = filter.NewFilter("sj8").UploadWsFilterTask(this.CountryCode, this.GetPhoneList())
	case constant.WsaTag:
		taskId, err = filter.NewFilter("sj8").UploadWsActiveTask(convert.Atoi(this.Extra), this.CountryCode, this.GetPhoneList())
	case constant.WssTag:
		taskId, err = filter.NewFilter("sj8").UploadWsSexTask(this.CountryCode, this.GetPhoneList())
	case constant.FbTag:
		taskId, err = filter.NewFilter("sj8").UploadFBFilterTask(this.CountryCode, this.GetPhoneList())
	case constant.InsTag:
		taskId, err = filter.NewFilter("sj8").UploadINSFilterTask(this.CountryCode, this.GetPhoneList())
	case constant.LinkTag:
		taskId, err = filter.NewFilter("sj8").UploadLinkFilterTask(this.CountryCode, this.GetPhoneList())
	case constant.LinksTag:
		taskId, err = filter.NewFilter("sj8").UploadLinkSexFilterTask(this.CountryCode, this.GetPhoneList())
	case constant.LinkStandardTag:
		taskId, err = filter.NewFilter("sj8").UploadLinkStandardFilterTask(this.CountryCode, this.GetPhoneList())
	case constant.ViberfTag:
		taskId, err = filter.NewFilter("sj8").UploadViberFilterTask(this.CountryCode, this.GetPhoneList())
	case constant.ViberaTag:
		taskId, err = filter.NewFilter("sj8").UploadViberActiveTask(convert.Atoi(this.Extra), this.CountryCode, this.GetPhoneList())
	case constant.AmazonTag:
		taskId, err = filter.NewFilter("sj8").UploadAmazonFilterTask(this.CountryCode, this.GetPhoneList())
	case constant.LineTag:
		taskId, err = filter.NewFilter("sj8").UploadLineFilterTask(this.CountryCode, this.GetPhoneList())
	case constant.LinesTag:
		taskId, err = filter.NewFilter("sj8").UploadLineSexTask(this.CountryCode, this.GetPhoneList())
	case constant.ZaloTag:
		taskId, err = filter.NewFilter("sj8").UploadZaloFilterTask(this.CountryCode, this.GetPhoneList())
	case constant.ZalosTag:
		taskId, err = filter.NewFilter("sj8").UploadZaloSexTask(this.CountryCode, this.GetPhoneList())
	case constant.TwitterTag:
		taskId, err = filter.NewFilter("sj8").UploadTwitterFilterTask(this.CountryCode, this.GetPhoneList())
	case constant.SkypeTag:
		taskId, err = filter.NewFilter("sj8").UploadSkypeFilterTask(this.CountryCode, this.GetPhoneList())
	case constant.TgTag:
		taskId, err = filter.NewFilter("sj8").UploadTgFilterTask(this.CountryCode, this.GetPhoneList())
	case constant.TgaTag:
		taskId, err = filter.NewFilter("sj8").UploadTgActiveTask(convert.Atoi(this.Extra), this.CountryCode, this.GetPhoneList())
	case constant.MomoTag:
		taskId, err = filter.NewFilter("sj8").UploadMomoFilterTask(this.CountryCode, this.GetPhoneList())
	case constant.MtnMomoTag:
		taskId, err = filter.NewFilter("sj8").UploadMtnMomoFilterTask(this.CountryCode, this.GetPhoneList())
	case constant.MicrosoftTag:
		taskId, err = filter.NewFilter("sj8").UploadMicrosoftFilterTask(this.CountryCode, this.GetPhoneList())
	case constant.TrueMoneyTag:
		taskId, err = filter.NewFilter("sj8").UploadTrueMoneyFilterTask(this.CountryCode, this.GetPhoneList())
	case constant.BotimTag:
		taskId, err = filter.NewFilter("sj8").UploadBotIMFilterTask(this.CountryCode, this.GetPhoneList())
	case constant.OperatorStandardTag:
		taskId, err = filter.NewFilter("sj8").UploadOperatorStandardFilterTask(this.CountryCode, this.GetPhoneList())
	case constant.OperatorPremiumTag:
		taskId, err = filter.NewFilter("sj8").UploadOperatorPremiumFilterTask(this.CountryCode, this.GetPhoneList())
	case constant.PhoneCheckTag:
		taskId, err = filter.NewFilter("sj8").UploadPhoneCheckFilterTask(this.CountryCode, this.GetPhoneList())
	case constant.TkTag:
		taskId, err = filter.NewFilter("sj8").UploadTkFilterTask(this.CountryCode, this.GetPhoneList())
	case constant.TkStandardTag:
		taskId, err = filter.NewFilter("sj8").UploadTkStandardFilterTask(this.CountryCode, this.GetPhoneList())
	}

	if err != nil {
		log.Err("doTaskData", this, err)
		data.DB().Exec(fmt.Sprintf("UPDATE %s SET Status=5,UpdateTime=?,Ret=? where id=?", getTableName(this.OTag)), time.Now().Unix(), err.Error(), this.Id)
		return err
	}
	this.TaskId = taskId
	_, err = data.DB().Exec(fmt.Sprintf("UPDATE %s SET Status=2,UpdateTime=?,TaskId=? where id=?", getTableName(this.OTag)), time.Now().Unix(), taskId, this.Id)
	return err
}

func (this *Op_Order) loopTaskDataDebug() {

	s := rand.In(10, 20)
	<-time.After(time.Second * time.Duration(s))
	fields := map[string]interface{}{
		"FCnt":       1000,
		"SCnt":       523,
		"Status":     3,
		"UpdateTime": int(time.Now().Unix()),
	}

	setsql := []string{}
	for key, value := range fields {
		setsql = append(setsql, fmt.Sprintf("%s=%v", key, value))
	}

	_, err := data.DB().Exec(fmt.Sprintf("UPDATE %s SET %s where id=?", getTableName(this.OTag), strings.Join(setsql, ",")), this.Id)
	if err != nil {
		log.Err(err)
	}
}

func (this *Op_Order) loopTaskData() {
	if *sieveDebug {
		this.loopTaskDataDebug()
		return
	}
ForEnd:
	for {
		select {
		case <-app.CTX.Done():
			break ForEnd
		default:
			log.Debug("loopTaskData", this.GetTagId())
			if this.Status == 1 {
				time.Sleep(time.Second * 5)
				continue
			}
			str, err := filter.NewFilter("sj8").CheckWsFilterTask(this.TaskId)
			if err == nil {
				retData := &filter.FilterCheckTaskData{}
				if err = json.Unmarshal([]byte(str), &retData); err == nil {
					fields := make(map[string]interface{})
					if this.FCnt != retData.FCnt {
						fields["FCnt"] = retData.FCnt
						this.FCnt = retData.FCnt
					}
					if this.SCnt != retData.SCnt {
						fields["SCnt"] = retData.SCnt
						this.SCnt = retData.SCnt
					}
					if retData.Status == "finish" {
						fields["Status"] = 3
					}
					if retData.Status == "refund" {
						fields["Ret"] = "'退款'"
						fields["Status"] = 5
					}
					this.updateTaskData(fields)
					if retData.Status == "finish" || retData.Status == "refund" {
						RemoveOrderTaskId(this.GetTagId())
						break ForEnd
					}
				}
			}
			if err != nil {
				log.Err("err", err, this)
			}
		}
		time.Sleep(time.Second * 3)
	}
}
func (this *Op_Order) updateTaskData(fields map[string]interface{}) {
	if len(fields) == 0 {
		return
	}
	fields["UpdateTime"] = int(time.Now().Unix())
	setsql := []string{}
	taskClose := false
	for key, value := range fields {
		setsql = append(setsql, fmt.Sprintf("%s=%v", key, value))

		// 判断状态Status 为 3 的时候才进行文件下载
		if key == "Status" && value == 3 {
			taskClose = true
		}
	}

	// 先下载文件，再更新状态，确保接口下载文件的时候，文件是存在的，而不是下载任务还在进行中...
	var err error
	if taskClose {
		//任务完成，需要下载文件
		phones := []string{}
		if this.SCnt > 0 {
			phones, err = filter.NewFilter("sj8").DownFilterTask(this.OTag, this.TaskId)
			if err != nil {
				log.Err(err, this.TaskId)
				return
			}

			err = util.WriteToTxt(uTime.FmtDate(time.Unix(this.CreateTime, 0), "Ymd"), this.GetTagId(), strings.Join(phones, "\n"))
			if err != nil {
				log.Err(err)
			}
		}

		//if this.SCnt != len(phones) {
		//	data.DB().Exec(fmt.Sprintf("UPDATE %s SET %s=? where id=?", getTableName(this.OTag), "SCnt"), len(phones), this.Id)
		//}

		dayTime := uTime.FmtDate(time.Unix(this.CreateTime, 0), "Ymd")
		preDir := util.GetFilterDir(dayTime)
		util.AutoCreatePath(preDir)
		xlsxFileName := filepath.Join(preDir, this.GetTagId()+".xlsx")
		csvFileName := filepath.Join(preDir, this.GetTagId()+".csv")

		_, err = filter.NewFilter("sj8").DownSexFilterTask(this.OTag, this.TaskId, xlsxFileName, csvFileName)
		if err != nil {
			log.Err(err)
		}
	}

	_, err = data.DB().Exec(fmt.Sprintf("UPDATE %s SET %s where id=?", getTableName(this.OTag), strings.Join(setsql, ",")), this.Id)
	if err != nil {
		log.Err(err)
	}
}
